/**
 * dxLogger module.
 * A simple, static logger that replaces `console.log`.
 * It provides multi-level logging and can be viewed in the corresponding VSCode plugin during debugging.
 *
 * Features:
 * - Three log levels: DEBUG, INFO, ERROR. All levels are always enabled.
 * - Supports logging various JavaScript data types, including objects and errors.
 * - Non-blocking log output to avoid performance impact.
 *
 * Usage:
 * - Import the logger: `import log from './dxLogger.js'`
 * - Use the logging methods: `log.info('Application started');`, `log.error('An error occurred:', new Error('test'));`
 */
const logger = {}
// The maximum length of the log message. In special cases, too long content can cause application crashes.
logger.max_length = 1024
import * as std from "std"
import dxMap from './dxMap.js'
let loggerMap = dxMap.get('__logger__')

/**
 * Enables or disables the debug mode for the logger.
 * When debug mode is enabled, output is flushed immediately after each log.
 * This is essential for seeing logs in real-time when the output is redirected
 * to a pipe (e.g., during VS Code debugging), but can impact performance and stability
 * on physical serial ports.
 *
 * @param {boolean} [isdebug=true] - Whether to enable or disable debug mode.
 * @example
 * // Enable debug mode for real-time logging
 * logger.setDebug(true);
 *
 * // Disable debug mode for production/stability testing
 * logger.setDebug(false);
 */
logger.setDebug = function (isdebug = true) {
    if (isdebug) {
        loggerMap.put('isdebug', true)
    } else {
        loggerMap.put('isdebug', false)
    }
}

/**
 * Logs a message at the DEBUG level.
 * @param {...*} data - The data to log. Can be multiple arguments of any type.
 * @example
 * logger.debug('User logged in:', { userId: 123 });
 */
logger.debug = function (...data) {
    log("DEBUG", data)
}

/**
 * Logs a message at the INFO level.
 * @param {...*} data - The data to log. Can be multiple arguments of any type.
 * @example
 * logger.info('Server started on port', 8080);
 */
logger.info = function (...data) {
    log("INFO", data)
}

/**
 * Logs a message at the ERROR level.
 * @param {...*} data - The data to log. Can be multiple arguments of any type.
 * @example
 * try {
 *   // ... some code that might fail
 * } catch (e) {
 *   logger.error('Operation failed:', e);
 * }
 */
logger.error = function (...data) {
    log("ERROR", data)
}
//-----------------------------------private----------------------
// Formats and prints the log message to standard output.
function log(level, messages) {
    let message = messages.map(msg => getContent(msg)).join(' ');
    //multi \n will cause vscode to not see the subsequent logs
    if (message.includes('\n\n')) {
        message = message.replace(/\n{2,}/g, '\n');
    }
    const content = `[${level} ${getTime()}]: ${message}`.trimEnd();
    try {
        if (content.length > logger.max_length) {
            std.puts(content.slice(0, logger.max_length - 3) + '...\n');
        } else {
            std.puts(content + '\n');
        }

        // The default behavior is to flush, which is suitable for real-time debugging (VSCode).
        // To disable flushing for stability testing on serial ports (MobaXterm),
        // explicitly call logger.setDebug(false).
        if (loggerMap.get('isdebug') !== false) {
            std.out.flush();
        }
    } catch (e) {
        // If even the fallback fails, there's nothing more we can do.
    }
}
// Converts any JavaScript value to a string for logging.
function getContent(message) {
    if (message === undefined) {
        return 'undefined'
    } else if (message === null) {
        return 'null'
    }
    if (typeof message === 'object') {
        if (Object.prototype.toString.call(message) === '[object Error]') {
            let errorString = message.message || 'Error';
            if (message.stack) {
                errorString += '\n' + message.stack;
            }
            return errorString;
        }
        return JSON.stringify(message)
    }
    return String(message);
}
// Generates a timestamp string in 'YYYY-MM-DD HH:mm:ss.ms' format.
function getTime() {
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const day = String(now.getDate()).padStart(2, '0');
    const hours = String(now.getHours()).padStart(2, '0');
    const minutes = String(now.getMinutes()).padStart(2, '0');
    const seconds = String(now.getSeconds()).padStart(2, '0');
    const milliseconds = String(now.getMilliseconds()).padStart(3, '0');
    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
}
if (globalThis && globalThis.console) {
    globalThis.console.log = logger.info
    globalThis.console.debug = logger.debug
    globalThis.console.error = logger.error
}
export default logger